﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Windows.Forms;
using System.Globalization;
using Kawanon.Foundation;

namespace Kawanon.QtHoney
{
	public static class Reflection
	{
		private const BindingFlags AllMembers =
			BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static;

		public static object GetMemberValue(object obj, string name, bool property)
		{
			return obj.GetType().InvokeMember(name,
				AllMembers | (property ? BindingFlags.GetProperty : BindingFlags.GetField),
				null, obj, null);
		}

		public static void SetMemberValue(object obj, string name, bool property, object val)
		{
			obj.GetType().InvokeMember(name,
				AllMembers | (property ? BindingFlags.SetProperty : BindingFlags.SetField),
				null, obj, new object[] { val });
		}

		public static void SetField(object obj, string name, object val)
		{
			SetMemberValue(obj, name, false, val);
		}

		public static object GetField(object obj, string name)
		{
			return GetMemberValue(obj, name, false);
		}

		public static void SetProperty(object obj, string name, object val)
		{
			SetMemberValue(obj, name, true, val);
		}

		public static object GetProperty(object obj, string name)
		{
			return GetMemberValue(obj, name, true);
		}

		private class Deleter : IDisposable
		{
			private bool m_disposed = false;
			private MethodInvoker m_fun;

			public Deleter(MethodInvoker fun)
			{
				m_fun = fun;
			}

			public void Dispose()
			{
				if (m_disposed)
				{
					throw new ObjectDisposedException(GetType().Name);
				}
				m_fun();
				m_disposed = true;
			}
		}

		public static void SetEventHandler(object obj, EventInfo eventInfo, bool addOrRemove,
			object handlerThis, MethodInfo handlerMethod)
		{
			MethodInfo methodInfo = addOrRemove ? eventInfo.GetAddMethod(true) : eventInfo.GetRemoveMethod(true);
			Type delegateType = eventInfo.EventHandlerType;
			Delegate fun = Delegate.CreateDelegate(delegateType, handlerThis, handlerMethod);
			methodInfo.Invoke(obj, new object[] { fun });
		}

		public static void SetEventHandler(object obj, string name, bool addOrRemove,
			object handlerThis, string handlerName)
		{
			EventInfo eventInfo = obj.GetType().GetEvent(name, AllMembers);
			MethodInfo handlerMethod = handlerThis.GetType().GetMethod(handlerName, AllMembers);
			SetEventHandler(obj, eventInfo, addOrRemove, handlerThis, handlerMethod);
		}

		public static IDisposable AddEventHandler(object obj, EventInfo eventInfo,
			object handlerThis, MethodInfo handlerMethod)
		{
			SetEventHandler(obj, eventInfo, true, handlerThis, handlerMethod);

			Deleter deleter = new Deleter(
				() => SetEventHandler(obj, eventInfo, false, handlerThis, handlerMethod));
			return deleter;
		}

		public static IDisposable AddEventHandler(object obj, string name,
			object handlerThis, string handlerName)
		{
			EventInfo eventInfo = obj.GetType().GetEvent(name, AllMembers);
			MethodInfo handlerMethod = handlerThis.GetType().GetMethod(handlerName, AllMembers);
			return AddEventHandler(obj, eventInfo, handlerThis, handlerMethod);
		}

		public static Attr GetEnumFieldAttribute<Attr>(object e)
			where Attr : Attribute
		{
			Type enumType = e.GetType();
			string name = Enum.GetName(enumType, e);
			if (name == null)
			{
				return null;
			}
			FieldInfo field = enumType.GetField(name);
			if (field == null)
			{
				throw new Exception("FieldInfo failed to get");
			}

			Type attrType = typeof(Attr);
			Attribute attr = Attribute.GetCustomAttribute(field, attrType);
			return (Attr)attr;
		}

		public static string GetEnumDisplayName(Enum e)
		{
			EnumDisplayNameAttribute attr = GetEnumFieldAttribute<EnumDisplayNameAttribute>(e);
			if (attr == null)
			{
				return null;
			}
			return attr.GetDisplayName(e, CultureInfo.CurrentCulture);
		}
	}
}
